第一道指令先從 Immediat 系列開始吧
指令系列會依照時間安排增減速度,
一方面是因為上一篇留下的技術債要想辦法 Refactor,
另一方面是要留時間提前準備接下來的內容。
根據 Andes RISC-V Conf’21 的資訊,
Tiny ONNC build 出來的執行檔可以在 Bare Metal 環境下執行,
這段時間會對相關的 Softwere Stack 進行研究,
預計會用 ONNC 或者 tiny ONNC 來為這篇系列文收尾。
期待這次的成果,一定是大拇指的拉!
指令格式如下:
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | funct3 | rd | pocode |
+-------------------------------------------+
ADDI 會把 rs1 + imm 存到 rd 裡面。
運算時遇到 Overflow 不進行任何處理,
直接把最低的 32 bit 放到 rd 內。
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| imm | rs1 | 000 | rd | 0010011 |
+-------------------------------------------+
沒錯, opcode 和 funct3 都沒有打錯,跟 ADDI 一模一樣,
從黑洞拿 0,加 0 再塞回去,官方認證的 NOP。
實做一道指令可以當兩道用,讚!
|31 20|19 15|14 12|11 7|6 0|
+-------------------------------------------+
| 0 | 0 | 000 | 0 | 0010011 |
+-------------------------------------------+
github 頁面 Tag: ITDay10
這次新增了 EXECUTOR 來管理 CPU 的執行部分,
在 CPU 內只會呼叫 execute 讓 EXECUTOR 執行 decoder 內的指令。
class EXECUTOR_INTERFACE
{
public:
virtual void execute() = 0;
void set_instruction_decoder(const std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> &instance)
{
instruction_decoder = instance;
}
void set_register_file(const std::shared_ptr<REGISTER_INTERFACE> &instance)
{
register_file = instance;
}
protected:
std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> instruction_decoder;
std::shared_ptr<REGISTER_INTERFACE> register_file;
};
這次實作了第一道指令:ADDI
同時這道指令也是 NOP,因為 destination 寫到 x0 並不會影響 x0 的值。
void EXECUTOR::execute()
{
if(instruction_decoder->get_opcode() == INSTRUCTION_DECODER_INTERFACE::OP_IMM &&
instruction_decoder->get_func3() == 0b000) {
auto rs1 = instruction_decoder->get_rs1();
auto rd = instruction_decoder->get_rd();
auto value = register_file->get_value_integer(rs1) + instruction_decoder->get_imm(31, 20);
register_file->set_value_integer(rd, value);
...
}
}
EXECUTOR 需要 memory、register file 和 instruction decoder 的配合才能工作,
這次先只把 instruction_decoder
和 register_file
放進去。
class EXECUTOR_INTERFACE
{
...
std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> instruction_decoder;
std::shared_ptr<REGISTER_INTERFACE> register_file;
};
前面的文章有提到,記憶體分成 explict 和 implict 存取,
這邊將 explice 存取由 EXECUTOR 處理, implict 存取則由 CPU 統一負責。
Instance 由 CPU 管理,所以在 setter 中做了對應的修改。
void CPU::set_register_file(const std::shared_ptr<REGISTER_INTERFACE> &instance)
{
...
executor->set_register_file(register_file);
...
}
void CPU::set_instruction_decoder(const std::shared_ptr<INSTRUCTION_DECODER_INTERFACE> &instance)
{
...
executor->set_instruction_decoder(instruction_decoder);
...
}
void CPU::set_executor(const std::shared_ptr<EXECUTOR_INTERFACE> &instance)
{
...
executor->set_instruction_decoder(instruction_decoder);
...
executor->set_register_file(register_file);
}
shared_ptr 和做 interface 的理由和之前一樣,不再贅述。